function nameComparator(left, right) {
	return left.name == right.name ? 0 : (left.name < right.name ? -1 : 1);
};

function Book(id, name, ownerId) {

	this.id = id;
	this.name = name;
	this.ownerId = ownerId;
	this.accounts = ko.observableArray();
	this.accountsVersion = ko.observable(-1);
	this.categories = ko.observableArray();
	this.categoriesVersion = ko.observable(-1);
	this.currencies = ko.observableArray();
	this.currenciesVersion = ko.observable(-1);
	this.tags = ko.observableArray();
	this.tagsVersion = ko.observable(-1);

	this.checkInterval = 10000;
	this.checkTimer = null;

	this.reset = function() {
		this.accounts = ko.observableArray();
		this.accountsVersion = ko.observable(-1);
		this.categories = ko.observableArray();
		this.categoriesVersion = ko.observable(-1);
		this.currencies = ko.observableArray();
		this.currenciesVersion = ko.observable(-1);
		this.tags = ko.observableArray();
		this.tagsVersion = ko.observable(-1);
		if(this.checkTimer != null) {
			clearTimeout(this.checkTimer);
		}
	}

	this.categoryBuilder = function(item, itemsMap) {
		var parent = null;
		if(item.parentId in itemsMap) {
			parent = this.categoryBuilder(itemsMap[item.parentId], itemsMap);
		}
		return new Category(item.id, item.name,  parent);
	};

	this.load = function(callback) {
		var self = this;
		var readyCount = 0;
		var onUpdate = function() {
			if(++readyCount === 4) {
				self.scheduleCheck();
				if(callback != null) {
					callback();
				}
			}
		};
		this.accounts.extend({ rateLimit: 200 });
		this.categories.extend({ rateLimit: 200 });
		this.currencies.extend({ rateLimit: 200 });
		this.tags.extend({ rateLimit: 200 });
		this.updateAccounts(onUpdate);
		this.updateCategories(onUpdate);
		this.updateCurrencies(onUpdate);
		this.updateTags(onUpdate);
	};

	this.updateAccounts = function(callback) {
		this.update(this.accounts, this.accountsVersion, "service/account/read.txt", null, callback);
	};

	this.updateCategories = function(callback) {
		var self = this;
		this.update(this.categories, this.categoriesVersion, "service/categories/read.txt", function(item, itemsMap) {
			return self.categoryBuilder(item, itemsMap);
		}, callback);
	};

	this.updateCurrencies = function(callback) {
		this.update(this.currencies, this.currenciesVersion, "service/currencies/read.txt", null, callback);
	};

	this.updateTags = function(callback) {
		this.update(this.tags, this.tagsVersion, "service/tags/read.txt", null, callback);
	};

	this.update = function(items, version, url, treeNodeBuilder, callback) {
		$.ajax({
			async: true,
			url: url,
			data: { bookId: this.id },
//			type: "GET",
//			contentType: "application/json",
			success: function (data, status, xhr) {
				var response = jQuery.parseJSON(data);
				items.removeAll();
				if(treeNodeBuilder != null) {
					var tmpItems = new Array();
					var itemsMap = {};
					response.items.forEach(function (item) {
						tmpItems.push(item);
						itemsMap[item.id] = item;
					});
					tmpItems.forEach(function(item) {
						items.push(treeNodeBuilder(item, itemsMap));
					});
				} else {
					response.items.forEach(function (item) {
						items.push(item);
					});
				}
				version(response.version);
//				console.log(items());
//				items.sort(nameComparator);
				if(callback != null) {
					callback();
				}
			},
			error: function () {
				console.log("!!! items read ERROR from URL: " + url);
			}
		});
	};

	this.checkUpdates = function() {
		var self = this;
		$.ajax({
			async: true,
			cache: false,
			url: "service/book/version.txt",
			data: { bookId: self.id },
			success: function (data, status, xhr) {
				var response = jQuery.parseJSON(data);
				if(self.accountsVersion() != response.accountsVersion) {
					console.log('accounts version changed');
					self.updateAccounts(null);
				}
				if(self.categoriesVersion() != response.categoriesVersion) {
					console.log('categories version changed');
					self.updateCategories(null);
				}
				if(self.currenciesVersion() != response.currenciesVersion) {
					console.log('currencies version changed');
					self.updateCurrencies(null);
				}
				if(self.tagsVersion() != response.tagsVersion) {
					console.log('tags version changed');
					self.updateTags(null);
				}

				self.scheduleCheck();
			},
			error: function () {
				//todo: implement book versions check error
				console.log("!!! book version check ERROR");
			}
		});
	};

	this.scheduleCheck = function() {
		var self = this;
		this.checkTimer = setTimeout(function() {
			self.checkUpdates();
		}, this.checkInterval);
	};

	this.isSame = function(book) {
		return book != null && this.id === book.id;
	};

	this.equals = function(book) {
		return book == null? false: (this.id === book.id && this.name === book.name && this.ownerId === book.ownerId);
	};
};